package process

import (
	monproto "gollect/proto"
	"sort"
	"time"

	"github.com/shirou/gopsutil/v3/process"
)

type procInfo struct {
	Pid    int32
	Ppid   int32
	Name   string
	CPU    float64
	Mem    float32
	Cmd    string
	Create int64
}

// Resource threshold constants
const (
	CPUThreshold    = 5.0 // 5% CPU usage threshold
	MemoryThreshold = 5.0 // 5% memory usage threshold
)

func CollectTopN(n int, interval time.Duration) []*monproto.JobInfo {
	procs, err := process.Processes()
	if err != nil {
		return nil
	}
	procMap := make(map[int32]*procInfo)
	childMap := make(map[int32][]int32)
	for _, p := range procs {
		ppid, _ := p.Ppid()
		name, _ := p.Name()
		cpu, _ := p.CPUPercent()
		mem, _ := p.MemoryPercent()
		cmd, _ := p.Cmdline()
		create, _ := p.CreateTime()
		procMap[p.Pid] = &procInfo{p.Pid, ppid, name, cpu, mem, cmd, create}
		childMap[ppid] = append(childMap[ppid], p.Pid)
	}
	jobRoots := make([]*procInfo, 0)
	for _, info := range procMap {
		if _, ok := procMap[info.Ppid]; !ok || info.Ppid == 1 {
			jobRoots = append(jobRoots, info)
		}
	}
	type jobAgg struct {
		root   *procInfo
		cpu    float64
		mem    float32
		uptime float64
		cmd    string
		name   string
	}
	var jobs []jobAgg
	for _, root := range jobRoots {
		cpu, mem, _ := aggregateJob(root.Pid, procMap, childMap)
		uptime := time.Since(time.Unix(0, root.Create*int64(time.Millisecond))).Seconds()
		jobs = append(jobs, jobAgg{root, cpu, mem, uptime, root.Cmd, root.Name})
	}
	sort.Slice(jobs, func(i, j int) bool {
		return jobs[i].cpu > jobs[j].cpu
	})
	var result []*monproto.JobInfo
	for i := 0; i < len(jobs); i++ {
		j := jobs[i]
		// Apply threshold filter: skip if both CPU and memory are below threshold
		if j.cpu < CPUThreshold && j.mem < MemoryThreshold {
			continue
		}
		// Stop after collecting n processes that meet the threshold
		if len(result) >= n {
			break
		}
		result = append(result, &monproto.JobInfo{
			RootPid:       j.root.Pid,
			Name:          j.name,
			Cmd:           j.cmd,
			Cpu:           float32(j.cpu / 100.0),
			Mem:           float32(j.mem / 100.0),
			UptimeSeconds: j.uptime,
		})
	}
	return result
}

func aggregateJob(pid int32, procMap map[int32]*procInfo, childMap map[int32][]int32) (float64, float32, []int32) {
	p, ok := procMap[pid]
	if !ok {
		return 0, 0, nil
	}
	cpu := p.CPU
	mem := p.Mem
	children := []int32{}
	for _, child := range childMap[pid] {
		ccpu, cmem, cchildren := aggregateJob(child, procMap, childMap)
		cpu += ccpu
		mem += cmem
		children = append(children, cchildren...)
	}
	return cpu, mem, children
}
